// 条款33：避免遮掩继承而来的名称

// 名称遮掩现象概述
// 在C++中，派生类中的名称会遮掩基类中的同名成员，这是C++的名称查找规则决定的。这种现象称为"名称遮掩"(name hiding)或"名称屏蔽"
// 名称遮掩的规则
// 1.作用域嵌套原则：
//      C++的名称查找从最内层作用域开始，向外层扩展
//      一旦在某个作用域找到名称，就停止查找
//      派生类是一个新的作用域，嵌套在基类作用域之内
// 2.全部遮掩原则：
//      派生类中的名称会遮掩基类中的所有同名成员
//      这包括所有重载函数，不管参数类型如何

// 名称遮掩的示例
class Base
{
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);

private:
    int x;
};

class Derived : public Base
{
public:
    virtual void mf1(); // 遮掩了Base::mf1()和Base::mf1(int)
    void mf3();         // 遮掩了Base::mf3()和Base::mf3(double)
    void mf4();

private:
    int x; // 遮掩了Base::x
};

Derived d;
d.mf1();     // 正常调用Derived::mf1()
d.mf1(10);   // 错误！Derived::mf1()遮掩了Base::mf1(int)
d.mf2();     // 正常调用Base::mf2()
d.mf3();     // 正常调用Derived::mf3()
d.mf3(10.5); // 错误！Derived::mf3()遮掩了Base::mf3(double)
// 这种遮掩行为可能导致意外的编译错误，尤其是当用户期望所有基类函数都可用时。

// 解决名称遮掩的方法
// 1. 使用using声明
// 最推荐的方法是使用using声明，将基类的名称引入派生类的作用域：
class Derived : public Base
{
public:
    using Base::mf1; // 引入所有名为mf1的基类函数
    using Base::mf3; // 引入所有名为mf3的基类函数

    virtual void mf1(); // 只重新定义无参版本
    void mf3();         // 只重新定义无参版本
};

// C++中的重载、重写、重定义和遮掩
// 1. 重载 (Overloading)
// 定义：在同一个作用域内，定义多个名称相同但参数列表不同的函数。
// 特点：
//      发生在同一个作用域内
//      函数名相同，参数列表不同（类型、数量或顺序）
//      返回类型可以相同也可以不同（但不能仅靠返回类型区分重载）
//      通过静态绑定（编译时）决定调用哪个版本
class MyClass
{
public:
    void print(int x);
    void print(double x);
    void print(const std::string &s);
};

// 2. 重写 (Override)
// 定义：派生类中定义一个与基类中虚函数具有相同签名的函数，用于替换基类的实现。
// 特点：
//      基类函数必须是虚函数（virtual）
//      派生类函数必须有完全相同的签名（函数名、参数列表、返回类型）
//      通过动态绑定（运行时）决定调用哪个版本
//      C++11引入了override关键字，帮助编译器检查是否正确重写
class Base
{
public:
    virtual void display() { std::cout << "Base" << std::endl; }
};

class Derived : public Base
{
public:
    void display() override { std::cout << "Derived" << std::endl; }
};

Base *ptr = new Derived();
ptr->display(); // 输出"Derived"

// 3. 重定义 (Redefinition)
// 定义：派生类中定义一个与基类中非虚函数同名的函数。
// 特点：
//      基类函数是非虚函数
//      派生类函数可以有相同或不同的参数列表
//      通过静态绑定决定调用哪个版本
//      如果参数列表相同，这是一种特殊形式的遮掩
//      如果参数列表不同，这既是重定义也是遮掩
class Base
{
public:
    void show() { std::cout << "Base" << std::endl; }
};

class Derived : public Base
{
public:
    void show() { std::cout << "Derived" << std::endl; } // 重定义
};

Base *ptr = new Derived();
ptr->show(); // 输出"Base"，因为是静态绑定

// 4. 遮掩 (Hiding)
// 定义：派生类中的名称会遮掩基类中的所有同名成员。
// 特点：
// 发生在不同作用域之间（派生类作用域遮掩基类作用域）
// 派生类中的名称会遮掩基类中的所有同名成员，不管参数列表如何
// 适用于函数、变量和其他命名实体
// 通过using声明可以避免遮掩
class Base
{
public:
    void func();
    void func(int);
};

class Derived : public Base
{
public:
    void func(); // 遮掩了Base::func()和Base::func(int)
};

Derived d;
d.func(); // 正常调用
          // d.func(5);   // 错误！Base::func(int)被遮掩了